<?
package("jpunit.textui");

import("japha.lang.System");
import("japha.io.PrintStream");

import("jpunit.framework.*");
import("jpunit.runner.*");
import("jpunit.ui.textui.ResultPrinter");

/**
 * $Id: TestRunner.php,v 1.6 2005/06/16 16:06:48 japha Exp $
 *
 * A command line based tool to run tests.
 * <pre>
 * java junit.textui.TestRunner [-wait] TestCaseClass
 * </pre>
 * TestRunner expects the name of a TestCase class as argument.
 * If this class defines a static <code>suite</code> method it 
 * will be invoked and the returned test is run. Otherwise all 
 * the methods starting with "test" having no arguments are run.
 * <p>
 * When the wait command line argument is given TestRunner
 * waits until the users types RETURN.
 * <p>
 * TestRunner prints a trace as the tests are executed followed by a
 * summary at the end. 
 *
 * @author <a href="mailto:gantt@cs.montana.edu">Ryan Gantt</a>
 * @version $Revision: 1.6 $
 */
class TestRunner extends BaseTestRunner 
{	
    /**
     * ResultPrinter
     */
	private $fPrinter;
	
	const SUCCESS_EXIT = 0;
	const FAILURE_EXIT = 1;
	const EXCEPTION_EXIT = 2;

	/**
	 * Constructs a TestRunner.
	 */
	public function __construct() 
	{
		$argv = func_get_args();
		if( !isset( $argv[0] ) )
		{
			$this->__construct( System::out() );
		}
		else if( $argv[0] instanceof PrintStream )
		{
			$this->__construct( new ResultPrinter( $argv[0] ) );
		}
		else if( $argv[0] instanceof ResultPrinter )
		{
			$this->fPrinter = $argv[0];
		}
	}
	
	/**
	 * Runs a suite extracted from a TestCase subclass.
	 */
	static public function run() 
	{
		$argv = func_get_args();
		if( !isset( $argv[0] ) )
		{
			throw new Exception("TestRunner::run requires at least one argument!");
		}
		else if( $argv[0] instanceof Test )
		{
			$runner = new TestRunner();
			return $runner->doRun( $argv[0] );
		}
		else if( $argv[0] instanceof _Class )
		{
			$this->__construct( new TestSuite( $argv[0] ) );
		}
	}

	/**
	 * Runs a single test and waits until the user
	 * types RETURN.
	 */
	static public function runAndWait( Test $suite ) 
	{
		$aTestRunner = new TestRunner();
		$aTestRunner->doRun( $suite, true );
	}

	/**
	 * Always use the StandardTestSuiteLoader. Overridden from
	 * BaseTestRunner.
	 */
	public function getLoader() 
	{
		return new StandardTestSuiteLoader();
	}

	/**
	 * Creates the TestResult to be used for the test run.
	 */
	protected function createTestResult() 
	{
		return new TestResult();
	}
	
	public function doRun( Test $suite, $wait = false) 
	{
		$result = $this->createTestResult();
		$result->addListener( $this->fPrinter );
		$startTime = System::currentTimeMillis();
		$suite->run( $result );
		$endTime = System::currentTimeMillis();
		$runTime = number_format( ( ( substr( $endTime, 0, 9 ) ) + ( substr( $endTime, -10 ) ) - ( substr( $startTime, 0, 9 ) ) - ( substr( $startTime, -10 ) ) ), 4 );
		$this->fPrinter->_print( $result, $runTime );
		$this->pause( $wait );
		return $result;
	}

	protected function pause( $wait ) 
	{
		if ( !$wait ) return;
		$fPrinter->printWaitPrompt();
		try 
		{
			System::$in->read();
		}
		catch(Exception $e) 
		{
		}
	}
	
	public static function main( $args ) 
	{
		$aTestRunner= new TestRunner();
		try 
		{
			$r = $aTestRunner->start( $args );
			if (!$r->wasSuccessful()) 
			{
				System::_exit( FAILURE_EXIT );
			}
			System::_exit( SUCCESS_EXIT );
		} 
		catch(Exception $e) 
		{
			System::$err->println($e->getMessage());
			System::_exit( EXCEPTION_EXIT );
		}
	}

	/**
	 * Starts a test run. Analyzes the command line arguments
	 * and runs the given test suite.
	 */
	protected function start( $args )
	{
		$testCase = new String("");
		$wait = false;
		
		for ($i = 0; $i < count($args); $i++) 
		{
			if ($args[$i]->equals("-wait"))
			{
				$wait = true;
			}
			else if ($args[$i]->equals("-c")) 
			{
				$testCase = extractClassName($args[++$i]);
			}
			else if ($args[$i]->equals("-v"))
			{
				System::$err->println( "JpUnit " . Version::id() . " by Kent Beck and Erich Gamma (port by Ryan Gantt)" );
			}
			else
			{
				$testCase = $args[ $i ];
			}
		}
		
		if ($testCase->equals("")) 
		{
			throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
		}

		try 
		{
			$suite = $this->getTest( $testCase );
			return $this->doRun( $suite, wait );
		}
		catch(Exception $e) 
		{
			throw new Exception("Could not create and run test suite: " . $e);
		}
	}
		
	protected function runFailed( $message ) 
	{
		System::$err->println( $message );
		System::_exit( FAILURE_EXIT );
	}
	
	public function setPrinter( ResultPrinter $printer ) 
	{
		$this->fPrinter = $printer;
	}
	
	public function testFailed( $status, Test $test, Throwable $t ){}
	public function testStarted( $testName ){}
	public function testEnded( $testName ){}
}
?>